﻿namespace Webinator
{
    using Enums;

    using Webinator.Toolboxes;

    /// <summary>
    /// Describes how to locate an element or collection of elements.
    /// </summary>
    public class Locator
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        /// <param name="findBy">
        /// The type.
        /// </param>
        /// <param name="value">The value.</param>
        /// <param name="use">Indicates which elements to use if a collection is found.</param>
        /// <param name="isPartial">
        /// Indicates whether <c>Value</c> is partial.
        /// </param>
        /// <param name="index">
        /// The index.
        /// </param>
        public Locator(FindBy findBy, string value, Use use, bool isPartial, int index)
        {
            FindBy = findBy;
            Use = use;
            Value = value;
            IsPartial = isPartial;
            Index = index;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        /// <param name="findBy">
        /// The type.
        /// </param>
        /// <param name="value">The value.</param>
        /// <param name="use">Indicates which elements to use if a collection is found.</param>
        /// <param name="isPartial">
        /// Indicates whether <c>Value</c> is partial.
        /// </param>
        public Locator(FindBy findBy, string value, Use use, bool isPartial)
        {
            FindBy = findBy;
            Use = use;
            Value = value;
            IsPartial = isPartial;
            Index = 0;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        /// <param name="findBy">
        /// The type.
        /// </param>
        /// <param name="value">The value.</param>
        /// <param name="use">Indicates which elements to use if a collection is found.</param>
        /// <param name="index">
        /// The index.
        /// </param>
        public Locator(FindBy findBy, string value, Use use, int index)
        {
            FindBy = findBy;
            Use = use;
            Value = value;
            IsPartial = false;
            Index = index;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        /// <param name="findBy">The type.</param>
        /// <param name="value">The value.</param>
        /// <param name="use">Indicates which elements to use if a collection is found.</param>
        public Locator(FindBy findBy, string value, Use use)
        {
            FindBy = findBy;
            Use = use;
            Value = value;
            IsPartial = false;
            Index = 0;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        /// <param name="findBy">
        /// The type.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="isPartial">
        /// Indicates whether <c>Value</c> is partial.
        /// </param>
        /// <param name="index">
        /// The index.
        /// </param>
        public Locator(FindBy findBy, string value, bool isPartial, int index)
        {
            FindBy = findBy;
            Value = value;
            IsPartial = isPartial;
            Index = index;
            Use = Use.One;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        /// <param name="findBy">
        /// The type.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="index">
        /// The index.
        /// </param>
        public Locator(FindBy findBy, string value, int index)
        {
            FindBy = findBy;
            Value = value;
            IsPartial = false;
            Index = index;
            Use = Use.One;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        /// <param name="findBy">
        /// The type.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="isPartial">
        /// Indicates whether <c>Value</c> is partial.
        /// </param>
        public Locator(FindBy findBy, string value, bool isPartial)
        {
            FindBy = findBy;
            Value = value;
            OriginalValue = Value;
            IsPartial = isPartial;
            Index = 0;
            Use = Use.One;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        /// <param name="findBy">
        /// The type.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        public Locator(FindBy findBy, string value)
        {
            FindBy = findBy;
            Value = value;
            OriginalValue = Value;
            IsPartial = false;
            Index = 0;
            Use = Use.One;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Locator"/> class.
        /// </summary>
        public Locator()
        {
            Index = 0;
            Use = Use.One;
        }

        /// <summary>
        /// Gets or sets the locator type.
        /// </summary>
        public FindBy FindBy { get; set; }

        /// <summary>
        /// Gets or sets the locator selector type if more than one element is found by the <c>Locator</c>:
        /// All: all elements, overrides <c>Index</c>.
        /// Any: any element, overrides <c>Index</c>.
        /// One (default): one element: you must further specify which one with the <c>Index</c> property.
        /// </summary>
        public Use Use { get; set; }

        /// <summary>
        /// Gets or sets the locator value.
        /// </summary>
        public string Value { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether <c>Value</c> is partial.
        /// </summary>
        public bool IsPartial { get; set; }
        
        /// <summary>
        /// Gets or sets the index, defaults to 0.
        /// If the locator returns multiple elements, this specifies which one to use.
        /// </summary>
        public int Index { get; set; }

        /// <summary>
        /// Gets or sets the original value.
        /// Used to retain <c>Value</c> if it's changed by <c>SetArgs</c>.
        /// </summary>
        private string OriginalValue { get; set; }

        /// <summary>
        /// Performs a string format on <c>Value</c> with the arguments provided.
        /// </summary>
        /// <param name="args">
        /// The args.
        /// </param>
        /// <returns>The modified Locator.</returns>
        public Locator SetArgs(params object[] args)
        {
            Value = string.Format(OriginalValue, args);
            return this;
        }

        /// <summary>
        /// Returns the string representation of this <c>Locator</c>.
        /// </summary>
        /// <returns>
        /// A string.
        /// </returns>
        public override string ToString()
        {
            var text = IsPartial ? "Partial " : string.Empty;
            text = string.Format("{0}{1}:{2}", text, FindBy, Value);

            if (Index == 0 && Use != Use.One)
            {
                text = string.Format("{0} [Use:{1}]", text, Use);
            }
            else if (Index > 0)
            {
                text = string.Format("{0} [Index:{1}]", text, Index);
            }

            return text;
        }

        public string Xpath()
        {
            return XPathToolbox.GetXpathFromLocator(this);
        }
    }
}